/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "cms_verify.h"
#include "cmscbb_cms_def.h"
#include "cmscbb_sdk.h"
#include "../x509/cmscbb_x509.h"
#include "../x509/cmscbb_verify_info.h"
#include "../asn1/cmscbb_asn1_decode.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../cmscbb_common/cmscbb_list.h"
#include "../cmscbb_common/cmscbb_pem_util.h"
#include "../cms/cmscbb_cms_parse.h"
#include "../cms/cmscbb_crl_compare.h"
#include "../pki/cmscbb_pki.h"
#include <string.h>

#if CMSCBB_WITHOUT_SECUREC == 0
#include "securec.h"
#endif

#if CMSCBB_IN_KERNEL
#include <linux/export.h>
#endif

#if CMSCBB_SUPPORT_FILE
CVB_STATIC CMSCBB_ERROR_CODE InternalReadFile(const CVB_CHAR* filePath, CVB_BYTE** data, CVB_INT32* size);
#endif

#define MAX_PKI_BUFF_LEN (2 * 1000000)

#if CMSCBB_NEED_RELOCATE
/*
 * Prototype    : InternalDoRelocateAll
 * Description  : Address redirection switch.
 * The values of static variable (CMSCBB_NEED_RELOCATE) for pointer types in some embedded environments are determined
 * at compile time.
 * However, only the offset of the relative 0 addresses of these pointers is recorded at compile time.
 * The runtime program loaded the base site is not starting from 0,
 * In this case, you need to turn on the address redirection feature, And when creating a context, you need to pass in
 * a real program-loading base address.
 * The function RelocateTemplate will overload in different cases.
 * Params
 *   [IN] reloc_off: which indicates  the offset of pointers' location
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CVB_VOID InternalDoRelocateAll(CVB_ULONG reloc_off)
{
    if (reloc_off == 0) {
        return;
    }

    RelocateTemplate(CmscbbGetCmscbbX509Cert(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbX509ExtensionEntry(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbPkcs7Content(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbTimestampInfo(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbAsnOcts(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbX509Crl(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbX509BasicConstraints(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbAsnBits(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbAsnOidBundle(), reloc_off);
#if CMSCBB_SUPPORT_INDIRECT_CRL
    RelocateTemplate(CmscbbGetX509IssuingDistPoint(), reloc_off);
#endif
    RelocateTemplate(CmscbbGetCmscbbEccSign(), reloc_off);
    RelocateTemplate(CmscbbGetCmscbbAsnEnum(), reloc_off);

    if (GetCvbOidReloced() == CVB_FALSE) {
        int iter;
        for (iter = 0; iter < (CVB_INT)GetCvbOidCount(); ++iter) {
            GetCvbOIDTable()[iter].asnOid.octs += reloc_off;
        }
        SetCvbOidReloced(CVB_TRUE);
    }
}
#endif

CMSCBB_ERROR_CODE CmscbbVerifyCreateCtx(CMSCBB_VRF_CTX* ctx, const CmscbbCtxCreateParams* params)
{
    CmscbbVrf* vrf = CVB_NULL;
    CMSCBB_ERROR_CODE ret;

    if (ctx == CVB_NULL || params == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    if (params->st_size != sizeof(CmscbbCtxCreateParams)) {
        return CMSCBB_ERR_CONTEXT_INVALID_STRUCT;
    }

    /* for ctx not created yet, so use functions directly from pParms */
    ret = CmscbbMallocWith0((CVB_VOID**)&vrf, sizeof(CmscbbVrf));
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbPkiInit(vrf);
    if (CVB_FAILED(ret)) {
        CmscbbFreeWithNullCheck(vrf);
        return ret;
    }
    /* to compatible with old version, the usage type is set  USAGE_BUILD_SIGNING by default */
    vrf->usageType = USAGE_BUILD_SIGNING;
    vrf->size = sizeof(CmscbbVrf);

    /* all finished */
    *ctx = (CMSCBB_VRF_CTX)vrf;

#if CMSCBB_DELAY_ADDRESS_SET
    CmscbbAsn1TemplInit();
    CmscbbCmsTemplInit();
    CmscbbX509TemplInit();
#endif

#if CMSCBB_NEED_RELOCATE
    InternalDoRelocateAll(params->relocBase);
#endif
    return CVB_SUCCESS;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyCreateCtx);
#endif

/*
 * Prototype    : InternalCheckVerifyOjbect
 * Description  : Check the context verify  is legal or not
 * Params
 *   [IN] vrf: data for verify context
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckVerifyOjbect(const CmscbbVrf* vrf)
{
    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_BASE;
    }
    if (vrf->size != sizeof(CmscbbVrf)) {
        return CMSCBB_ERR_CONTEXT_INVALID_STRUCT;
    }
    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckBufLen(CVB_UINT32 bufLength)
{
    if (bufLength > MAX_PKI_BUFF_LEN) {
        CVB_LOG_ERROR(CMSCBB_ERR_SYS_BUF_TOO_LARGE, CVB_NULL);
        return CMSCBB_ERR_SYS_BUF_TOO_LARGE;
    }
    return CVB_SUCCESS;
}

#if CMSCBB_SUPPORT_FILE
/*
 * Prototype    : InternalCheckParamVerifyAddCertFile
 * Description  :  When File Interface switch (CMSCBB_SUPPORT_FILE) is turned on,
 * check the context verify  is legal , then check cert file is not null
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] certFile: cert file data
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyAddCertFile(const CmscbbVrf* vrf, const CVB_CHAR* certFile)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (certFile == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbVerifyAddCertFile(CMSCBB_VRF_CTX ctx, const CVB_CHAR* certFile)
{
    CVB_BYTE* data = CVB_NULL;
    CVB_INT32 fileSize = 0;
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    ret = InternalCheckParamVerifyAddCertFile(vrf, certFile);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = InternalReadFile(certFile, &data, &fileSize);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbVerifyAddCert(ctx, data, (CVB_UINT32)fileSize);
    CmscbbFreeWithNullCheck(data);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
    }

    return ret;
}
#endif

/*
 * Prototype    : InternalAddCertToStore
 * Description  : Add non-ca certificates into store
 * Params
 *   [IN] vrf: data for verify context
 *   [IN,OUT] certList: a list of x509cert
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalAddCertToStore(const CmscbbVrf* vrf, const LIST_OF(CmscbbX509Cert) *certList,
    CVB_BOOL allowRoot)
{
    CMSCBB_ERROR_CODE ret = CVB_SUCCESS;
    CVB_INT iter = 0;

    if (certList->num == 0) {
        return CMSCBB_ERR_UNDEFINED;
    }

    for (; iter < (CVB_INT)certList->num; ++iter) {
        CmscbbX509Cert* cert = certList->data[iter];
        CVB_BOOL isSelfSigned = CVB_FALSE;

        if (cert == CVB_NULL) {
            continue;
        }

        if (!allowRoot) {
            ret = CmscbbX509IsSelfSigned(cert, &isSelfSigned);
            if (CVB_FAILED(ret)) {
                CVB_LOG_ERROR(ret, "Failed to check self sign for certificate.");
                break;
            }
            if (isSelfSigned == CVB_TRUE) {
                CVB_LOG_WARNING(0, "There is a root certificate, which is not allowed when add crl or cms.");
                continue;
            }
        }

        ret = CmscbbPkiStoreAddCert(vrf, cert);
        if (ret == CMSCBB_ERR_PKI_CERT_ALREADY_EXIST) {
            CVB_LOG_INFO(0, "There is a duplicate certificate.");
            ret = 0;
        }
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(ret, "Failed to add certificate into PKI context.");
            break;
        }
    }

    return ret;
}

/*
 * Prototype    : InternalCheckParamVerfiyAddCert
 * Description  : Check verify context and X509Certificate
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert: X509Certificate data
 *   [IN] certLength : length of X509Certificate data
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyAddCert(const CmscbbVrf* vrf, const CVB_BYTE* cert,
    CVB_UINT32 certLength)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (cert == CVB_NULL || certLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    return InternalCheckBufLen(certLength);
}

CMSCBB_ERROR_CODE CmscbbVerifyAddCert(CMSCBB_VRF_CTX ctx, const CVB_BYTE* cert, CVB_UINT32 certLength)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    LIST_OF(CmscbbX509Cert) certList = {0};
    CmscbbListDummy* rawSet = CVB_NULL;

    ret = InternalCheckParamVerifyAddCert(vrf, cert, certLength);
    if (CVB_FAILED(ret)) {
        return ret;
    }
#if (CMSCBB_SUPPORT_PEM || CMSCBB_CACHE_ASN_DATA)
    rawSet = &vrf->rawSet;
#endif
    /* ignore the CRL */
    ret = CmscbbDecodeCertCrl(rawSet, cert, certLength, &certList, CVB_NULL);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalAddCertToStore(vrf, &certList, CVB_TRUE);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
CVB_ERR:
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyAddCert);
#endif

#if CMSCBB_SUPPORT_FILE
/*
 * Prototype    : InternalCheckParamVerifyAddCrlFile
 * Description  : Check verify context and crl file
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] crlFile: crl file data
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:09  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyAddCrlFile(const CmscbbVrf* vrf, const CVB_CHAR* crlFile)
{
    if (vrf == CVB_NULL || crlFile == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (vrf->size != sizeof(CmscbbVrf)) {
        return CMSCBB_ERR_CONTEXT_INVALID_STRUCT;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbVerifyAddCrlFile(CMSCBB_VRF_CTX ctx, const CVB_CHAR* crlFile)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BYTE* data = CVB_NULL;
    CVB_INT32 fileBytes = 0;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;

    ret = InternalCheckParamVerifyAddCrlFile(vrf, crlFile);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = InternalReadFile(crlFile, &data, &fileBytes);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbVerifyAddCrl(ctx, data, (CVB_UINT32)fileBytes);
    CmscbbFreeWithNullCheck(data);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}
#endif

/*
 * Prototype    : InternalAddCrlToStore
 * Description  : Add CRL list into store
 * Params
 *   [IN] vrf: data for verify context
 *   [IN,OUT] crlList: a list of X509crl
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:06  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalAddCrlToStore(const CmscbbVrf* vrf, const LIST_OF(CmscbbX509Crl) *crlList)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter = 0;

    for (; iter < (CVB_INT)crlList->num; ++iter) {
        CmscbbX509Crl* crl = crlList->data[iter];
        if (crl == CVB_NULL) {
            continue;
        }

        ret = CmscbbPkiStoreAddCrl(vrf, crl);
        if (ret == CMSCBB_ERR_PKI_CRL_ALREADY_EXIST) {
            CVB_LOG_INFO(ret, "CRL already existed.");
        } else if (CVB_FAILED(ret)) {
            return ret;
        }
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalCheckParamVerifyAddCrl
 * Description  : Add CRL list into store
 * Params
 *   [IN] vrf: data for verify context
 *   [IN,OUT] crlList: a list of X509crl
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:06  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyAddCrl(const CmscbbVrf* vrf, const CVB_BYTE* crlAdd,
    CVB_UINT32 crlLength)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (crlAdd == CVB_NULL || crlLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    return InternalCheckBufLen(crlLength);
}

CMSCBB_ERROR_CODE CmscbbVerifyAddCrl(CMSCBB_VRF_CTX ctx, const CVB_BYTE* crl, CVB_UINT32 crlLength)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    LIST_OF(CmscbbX509Cert) certList = {0};
    LIST_OF(CmscbbX509Crl) crlList = {0};
    CmscbbListDummy* rawSet = CVB_NULL;

    ret = InternalCheckParamVerifyAddCrl(vrf, crl, crlLength);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if (vrf->crlFrozen) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CRL_POOL_FROZEN, CVB_NULL);
        return CMSCBB_ERR_PKI_CRL_POOL_FROZEN;
    }

#if (CMSCBB_SUPPORT_PEM || CMSCBB_CACHE_ASN_DATA)
    rawSet = &vrf->rawSet;
#endif
    ret = CmscbbDecodeCertCrl(rawSet, crl, crlLength, &certList, &crlList);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret) || crlList.num == 0, CMSCBB_ERR_PKI_CRL_DECODE);

    ret = InternalAddCrlToStore(vrf, &crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalAddCertToStore(vrf, &certList, CVB_FALSE);
    if (CVB_FAILED(ret)) {
        CVB_LOG_WARNING(ret, "Failed to add certificate into store.");
        ret = 0; /* ignore error */
    }

CVB_ERR:
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyAddCrl);
#endif

#if CMSCBB_SUPPORT_FILE
/*
 * Prototype    : InternalCloseFile
 * Description  : Close file when read file and update context is over
 * Params
 *   [IN] fpSrc: a file handle
 * Return Value : CVB_STATIC CVB_VOID
 *   Date              Author     Modification
 *   2015/11/10 10:06  tangzonglei  Create
 */
CVB_STATIC CVB_VOID InternalCloseFile(CVB_FILE_HANDLE fpSrc)
{
    CMSCBB_ERROR_CODE retcode = CmscbbFileClose(fpSrc);
    if (CVB_FAILED(retcode)) {
        CVB_LOG_WARNING(retcode, NULL);
    }
}

/*
 * Prototype    : InternalUpdateContent
 * Description  : Read content file and update into verify context
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] srcfile: source file to update signature context
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:26  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalUpdateContent(CmscbbVrf* vrf, const CVB_CHAR* srcfile)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 read;
    CVB_FILE_HANDLE fpSrc;
    CVB_UINT64 srcFileSize;
    const CVB_UINT32 readBufLen = 4096;
    CVB_BYTE* readBuf = CVB_NULL;
    CMSCBB_VRF_CTX ctx = (CMSCBB_VRF_CTX)vrf;

    if (srcfile == CVB_NULL || *srcfile == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_SYS_FILE_OPEN, "Empty file path.");
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    fpSrc = CmscbbFileOpen(srcfile, (const CVB_CHAR*)"rb");
    if (fpSrc == CVB_NULL) {
        CVB_LOG_ERROR1(CMSCBB_ERR_SYS_FILE_OPEN, "Can't open file '%s'.", srcfile);
        return CMSCBB_ERR_SYS_FILE_OPEN;
    }

    srcFileSize = CmscbbFileGetSize(fpSrc);
    CVB_GOTO_ERR_WITH_LOG_IF(srcFileSize == 0, CMSCBB_ERR_SYS_FILE_GET_SIZE);
    CVB_GOTO_ERR_WITH_LOG_IF(((CVB_UINT64)(-1)) <= srcFileSize, CMSCBB_ERR_SYS_FILE_TOO_LARGE);

    ret = CmscbbMalloc((CVB_VOID**)(&readBuf), readBufLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    read = (CVB_UINT32)CmscbbFileRead(readBuf, readBufLen, fpSrc);
    while (read != 0) {
        ret = CmscbbVerifyDetachSignatureUpdate(ctx, readBuf, (CVB_INT32)read);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);

        read = (CVB_UINT32)CmscbbFileRead(readBuf, readBufLen, fpSrc);
    }
CVB_ERR:
    InternalCloseFile(fpSrc);
    CmscbbFreeWithNullCheck(readBuf);
    return ret;
}


/*
 * Prototype    : InternalCheckParamVerifyDetachSignatureQuick
 * Description  : When  verifying a separate signature,it needs a check for some parameters
 * Params
 *   [IN] ctx: Validation context
 *   [IN] srcfile: Verify the source file path of the signature file
 *   [IN] sigfile: CMS-formatted signature file path
 *   [OUT] res: Return validation res, 1 is passed, 0 is not passed
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:26  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyDetachSignatureQuick(const CmscbbVrf* vrf,
    const CVB_CHAR* srcfile, const CVB_CHAR* sigfile, const CVB_INT32* res)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (srcfile == CVB_NULL || sigfile == CVB_NULL || res == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbVerifyDetachSignatureQuick(CMSCBB_VRF_CTX ctx, const CVB_CHAR* srcfile,
    const CVB_CHAR* sigfile, CVB_INT32* res)
{
    CMSCBB_ERROR_CODE ret;
    CMSCBB_ERROR_CODE returnValue;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CVB_BYTE* sig = CVB_NULL;
    CVB_INT32 sigLen = 0;

    ret = InternalCheckParamVerifyDetachSignatureQuick(vrf, srcfile, sigfile, res);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    *res = CVB_FALSE;
    ret = InternalReadFile(sigfile, &sig, &sigLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbVerifyDetachSignatureBegin(ctx, sig, (CVB_UINT32)sigLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalUpdateContent(vrf, srcfile);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbVerifyDetachSignatureFinal(ctx, res);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
CVB_ERR:
    returnValue = CmscbbResetCert(ctx, CVB_FALSE);
    if (CVB_FAILED(returnValue)) {
        CVB_LOG_ERROR(returnValue, "reset cert failed!");
        ret = CMSCBB_ERR_CONTEXT_RESET_FAILED;
    }
    if (vrf->isHasCrl) {
        returnValue = CmscbbResetCrl(ctx);
        if (CVB_FAILED(returnValue)) {
            CVB_LOG_ERROR(returnValue, "reset crl failed!");
            ret = CMSCBB_ERR_CONTEXT_RESET_FAILED;
        }
        vrf->isHasCrl = CVB_FALSE;
    }
    CmscbbFreeWithNullCheck(sig);
    return ret;
}
#endif

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE

/*
 * Prototype    : InternalGetDigestFromSignerInfo
 * Description  : get digest value from signer info
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] si: pkcs7 signer information
 *   [OUT] pkcs7DigestInfo: digest information from pkcs7 signer information
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 10:54  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalGetDigestFromSignerInfo(const CmscbbPkcs7SignedInfo* si,
    CmscbbVerifyDigestInfo** pkcs7DigestInfo)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVerifyDigestInfo* digestInfo = CVB_NULL;

    *pkcs7DigestInfo = CVB_NULL;

    ret = CmscbbMallocWith0((CVB_VOID**)&digestInfo, sizeof(CmscbbVerifyDigestInfo));
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    digestInfo->digestAlgID = CmscbbGetHashAoidFromSign(&(si->digestAlgo.algorithm));
    CVB_GOTO_ERR_WITH_LOG_IF(digestInfo->digestAlgID == AOID_UNKNOWN, CMSCBB_ERR_PKI_CMS_DIGEST_ALGO_NOT_SUPPORT);

    digestInfo->digestSize = si->authAttrs.hashValue.len;
    CVB_GOTO_ERR_WITH_LOG_IF(si->authAttrs.hashValue.len > CMSCBB_MAX_DIGEST_SIZE, CMSCBB_ERR_SYS_MEM_COPY);

    ret = (CMSCBB_ERROR_CODE)memcpy_s(digestInfo->digest, CMSCBB_MAX_DIGEST_SIZE, si->authAttrs.hashValue.octs,
        si->authAttrs.hashValue.len);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    *pkcs7DigestInfo = digestInfo;
    return ret;
CVB_ERR:
    InternalFreeMdInfo(digestInfo);
    return ret;
}
#endif

#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
/*
 * Prototype    : InternalCreateVerifyHandle
 * Description  : internal create rsa verify handler
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] si: pkcs7 signer information
 *   [IN] cert: X509 certificate
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 * Remarks      : this used to verify CMS without signed attribute
 *   Date              Author     Modification
 *   2015/11/10 10:56  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCreateVerifyHandle(CmscbbVrf* vrf, const CmscbbPkcs7SignedInfo* si,
    const CmscbbX509Cert* cert)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVerifyDigestInfo* digestInfo = CVB_NULL;
    CMSCBB_CRYPTO_VRF_CTX vrfCtx = CVB_NULL;
    CmscbbKeyAndAlgInfo *verifyInfo = NULL;
    ret = CmscbbMallocWith0((CVB_VOID**)&digestInfo, sizeof(CmscbbVerifyDigestInfo));
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbCreateVerifyInfo(&verifyInfo);
    if (CVB_FAILED(ret)) {
        InternalFreeMdInfo(digestInfo);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbGetCmsVerifyInfo(si, cert, verifyInfo);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbCryptoVerifyCreateCtx(&vrfCtx);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

#if CMSCBB_SUPPORT_RSAPSS
    ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo);
#else
    ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo->e, verifyInfo->n, verifyInfo->hashAlg);
#endif
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    /* not support RSA encryption length more than 2048 bit */
    CVB_GOTO_ERR_WITH_LOG_IF(si->encryptedDigest.len > CMSCBB_MAX_CRYPT_SIZE, CMSCBB_ERR_PKI_CRYPTO_DIGEST_INIT);

    ret = (CMSCBB_ERROR_CODE)memcpy_s((CVB_VOID*)digestInfo->signature, CMSCBB_MAX_CRYPT_SIZE,
        si->encryptedDigest.octs, si->encryptedDigest.len);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CMSCBB_LIST_ADD(&(vrf->vrfProc.mdInfoList), digestInfo);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    digestInfo->signatureLen = si->encryptedDigest.len;
    digestInfo->vrfCtx = vrfCtx;
    CmscbbDestoryVerifyInfo(verifyInfo);
    return ret;
CVB_ERR:
    InternalFreeMdInfo(digestInfo);
    CmscbbDestoryVerifyInfo(verifyInfo);
    CmscbbCryptoVerifyDestroyCtx(vrfCtx);
    return ret;
}
#endif

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
/*
 * Prototype    : InternalAddSignerInfoToVerifyProcess
 * Description  : internal create hash handler
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] si: pkcs7 signer information
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 * Remarks      : this used to verify CMS with signed attributes
 *   Date              Author     Modification
 *   2015/11/10 11:03  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalAddSignerInfoToVerifyProcess(CmscbbVrf* vrf, const CmscbbPkcs7SignedInfo* si)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVerifyDigestInfo* digestInfo = CVB_NULL;
    CmscbbVerifyDigestInfo* exist = CVB_NULL;
    CMSCBB_CRYPTO_MD_CTX mdCtx = CVB_NULL;
    CVB_INT iter = 0;

    ret = InternalGetDigestFromSignerInfo(si, &digestInfo);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    /* check if already have the signature with same digest algorithm */
    for (; iter < (CVB_INT)vrf->vrfProc.mdInfoList.num; ++iter) {
        CmscbbVerifyDigestInfo* tempDi = vrf->vrfProc.mdInfoList.data[iter];
        if (tempDi == CVB_NULL) {
            continue;
        }
        if (tempDi->digestAlgID == digestInfo->digestAlgID) {
            CVB_GOTO_ERR_WITH_LOG_IF(tempDi->digestSize != digestInfo->digestSize,
                CMSCBB_ERR_PKI_CMS_DIGEST_VALUE_CONFLICT);
            CVB_GOTO_ERR_WITH_LOG_IF(0 != CmscbbMemCmp(tempDi->digest, digestInfo->digest,
                digestInfo->digestSize), CMSCBB_ERR_PKI_CMS_DIGEST_VALUE_CONFLICT);
            exist = tempDi;
            break;
        }
    }

    if (exist == CVB_NULL) {
        ret = CmscbbMdCreateCtx(&mdCtx);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);

        digestInfo->digestCtx = mdCtx;
        ret = CmscbbMdInit(mdCtx, (CVB_UINT32)digestInfo->digestAlgID);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);

        ret = CMSCBB_LIST_ADD(&(vrf->vrfProc.mdInfoList), digestInfo);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    } else {
        InternalFreeMdInfo(digestInfo);
    }

    return ret;
CVB_ERR:
    InternalFreeMdInfo(digestInfo);
    return ret;
}
#endif

/*
 * Prototype    : InternalExtractCertsIntoVerifyContext
 * Description  : extract certificates from signer info into verify context
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] p7signed: pckcs7 context info
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 11:22  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalExtractCertsAndCrlsIntoVerifyContext(CmscbbVrf* vrf,
    const CmscbbPkcs7Content* p7signed)
{
    CMSCBB_ERROR_CODE ret = CVB_SUCCESS;
    const LIST_OF(CmscbbX509Cert)* certs = &(p7signed->signedData.certificates.certs);
    const LIST_OF(CmscbbX509Crl)* crls = &(p7signed->signedData.crls.crls);

    if (certs->num > 0) {
        ret = InternalAddCertToStore(vrf, certs, CVB_FALSE);
        if (CVB_FAILED(ret)) {
            return ret;
        }
    } else {
        CVB_LOG_INFO(0, "No certificate contained in the CMS.");
    }

    if (crls->num > 0) {
        vrf->isHasCrl = CVB_TRUE;
        ret = InternalAddCrlToStore(vrf, crls);
        if (CVB_FAILED(ret)) {
            return ret;
        }
    } else {
        CVB_LOG_INFO(0, "No crl contained in the CMS.");
    }

    return ret;
}

/*
 * Prototype    : InternalHashContent
 * Description  : get hash value of the content
 * Params
 *   [IN] pbContent:content object
 *   [IN] nContent: the length of context object
 *   [IN] hashAlgoId: ASN object hash id
 *   [OUT] digest: digest object
 *   [OUT] digestLen: the length of digest object
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 11:29  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalHashContent(const CVB_BYTE* content, CVB_UINT32 contentLen,
    CmscbbAoids hashAlgoId, CVB_BYTE* digest, CVB_UINT32* digestLen, const CVB_UINT32* maxLength)
{
    CMSCBB_ERROR_CODE ret;
    CMSCBB_CRYPTO_MD_CTX mdCtx = CVB_NULL;

    ret = CmscbbMdCreateCtx(&mdCtx);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbMdInit(mdCtx, (CVB_UINT32)hashAlgoId);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMdUpdate(mdCtx, content, contentLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMdFinal(mdCtx, digest, digestLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

CVB_ERR:
    CmscbbMdDestoryCtx(mdCtx);
    return ret;
}

/*
 * Prototype    : InternalVerifyTstInfo
 * Description  : verify TST info in timestamp
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] encodedTsInfo: encoded content in timestamp, which should be CmscbbTimestampInfo
 *   [IN] si: signer info in CMS
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 11:29  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyTstInfo(CmscbbVrf* vrf, const CmscbbAsnOcts* encodedTsInfo,
    const CmscbbPkcs7SignedInfo* si, CmscbbPkcs7Content* p7Ts)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbTimestampInfo* tstInfo = CVB_NULL;
    CmscbbAoids hashAlgoId;
    CVB_BYTE digest[CMSCBB_MAX_DIGEST_SIZE] = {0};
    CVB_UINT32 digestLen = 0;
    CVB_UINT32 digestMaxLen = CMSCBB_MAX_DIGEST_SIZE;

    CVB_UINT32 decodedLen = 0;
    CmscbbDatetime dateTs = {0};
    CVB_TIME_T timeSign;

    CmscbbAsn1Item *tstTempl = CmscbbGetCmscbbTimestampInfo();
    /* decode TST info */
    ret = CmscbbAsnDecode(encodedTsInfo->octs, encodedTsInfo->len, tstTempl, CVB_ASN_NORMAL,
        (CVB_VOID**)&tstInfo, &decodedLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    if (AOID_PKCS7_TSTINFO != CmscbbFindAoid(&p7Ts->signedData.content.type)) {
        ret = CMSCBB_ERR_PKI_TST_WRONG_ENCAP_TYPE;
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_TST_WRONG_ENCAP_TYPE, "the timestamp eContentType not TSTInfo.");
        goto CVB_ERR;
    }
    hashAlgoId = CmscbbGetHashAoidFromSign(&(tstInfo->msgImprint.hashAlgo.algorithm));
    CVB_GOTO_ERR_WITH_LOG_IF(hashAlgoId == AOID_UNKNOWN, CMSCBB_ERR_PKI_CERT_SIG_ALGO);

    /* hash signature value in parent(CMS) signer info */
    ret = InternalHashContent(si->encryptedDigest.octs, si->encryptedDigest.len, hashAlgoId, digest, &digestLen,
        &digestMaxLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    if (tstInfo->msgImprint.hashValue.len != digestLen || CmscbbMemCmp(tstInfo->msgImprint.hashValue.octs,
        digest, digestLen) != 0) {
        ret = CMSCBB_ERR_PKI_TST_INFO_VERIFY;
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_TST_INFO_VERIFY, "the timestamp not match with target cms.");
        goto CVB_ERR;
    }

    /* set timestamp time into verify parameter */
    ret = CmscbbConvertFromTime(&(tstInfo->time), &dateTs);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    timeSign = CmscbbMktime(&dateTs);
    vrf->baseTime = timeSign;
CVB_ERR:
    CmscbbFreeWithNullCheck(tstInfo);
    return ret;
}

/*
 * Prototype    : InternalVerifyIssuerCert
 * Description  : verify certificate as an issuer
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert: X509 certificate
 *   [IN] expExtKu: expected key usage
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/12/26 10:17  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyIssuerCert(CmscbbVrf* vrf, CmscbbX509Cert* cert, CVB_UINT32 expExtKu)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BOOL isCertValid = CVB_FALSE;

    if (((CVB_UINT32)cert->toBeSigned.extensions.ku & CMSCBB_X509_KU_DIGITAL_SIGNATURE) == 0 ||
        ((CVB_UINT32)cert->toBeSigned.extensions.exku & expExtKu) == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE);
    }

    ret = CmscbbPkiVerifyCert(vrf, cert, CVB_TRUE, expExtKu == CMSCBB_PKI_XKU_TIMESTAMP ? CVB_TRUE : CVB_FALSE,
        &isCertValid);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    if (isCertValid == CVB_FALSE) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CERT_VERIFY_FAILED, CVB_NULL);
        return CMSCBB_ERR_PKI_CERT_VERIFY_FAILED;
    }

    return CVB_SUCCESS;
}

#if !CMSCBB_SUPPORT_DIGEST_STREAM_MODE
CVB_STATIC CMSCBB_ERROR_CODE InternalCryptoVerify(CMSCBB_CRYPTO_VRF_CTX vrfCtx, const CmscbbPkcs7SignedInfo* si,
    CVB_BYTE tagCode, CVB_INT* result)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BYTE* contentTemp = CVB_NULL;

    ret = CmscbbMallocWith0((CVB_VOID**)&contentTemp, si->rawSigned.len + 1);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "CmscbbMallocWith0 failed!");

    contentTemp[0] = tagCode;
    ret = (CMSCBB_ERROR_CODE)memcpy_s((CVB_VOID *) &(contentTemp[1]), si->rawSigned.len - 1,
        (const CVB_VOID *)(si->rawSigned.octs + 1), si->rawSigned.len - 1);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Failed to copy rawSigned!");

    contentTemp[si->rawSigned.len] = '\0';

    /* the tag type of signed attribute is implicit, so send real tag code */
    ret = CmscbbCryptoVerifyUpdate(vrfCtx, contentTemp, si->rawSigned.len);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "verify context update failed.");

    ret = CmscbbCryptoVerifyFinal(vrfCtx, si->encryptedDigest.octs, si->encryptedDigest.len, result);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "verify context final failed.");

    CmscbbFreeWithNullCheck(contentTemp);
    return CVB_SUCCESS;

CVB_ERR:
    if (contentTemp != CVB_NULL) {
        CmscbbFreeWithNullCheck(contentTemp);
    }
    return ret;
}
#endif

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
#define TAG_MASK 0xFF
#define CLS_BIT_POS 6
#define FORM_BIT_POS 5
/*
 * Prototype    : CmscbbPkcs7DetachedSignatureVerify
 * Description  : verify certificate
 * Params
 *   [IN] si: signer info in CMS
 *   [IN] cert: X509 certificate
 *   [OUT] result: verify res
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/12/26 10:17  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE CmscbbPkcs7DetachedSignatureVerify(const CmscbbPkcs7SignedInfo* si,
    const CmscbbX509Cert* cert, CVB_INT* result)
{
    CMSCBB_ERROR_CODE ret;
    CMSCBB_CRYPTO_VRF_CTX vrfCtx = CVB_NULL;
    CVB_BYTE tagCode;
    CmscbbKeyAndAlgInfo *verifyInfo = NULL;

    if ((si == CVB_NULL) || (cert == CVB_NULL) || (result == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbCreateVerifyInfo(&verifyInfo);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbGetCmsVerifyInfo(si, cert, verifyInfo);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    /* use public key to verify signer info */
    ret = CmscbbCryptoVerifyCreateCtx(&vrfCtx);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    *result = CVB_FALSE;

    {
        tagCode = TAG_MASK & (CVB_BYTE)(((((CVB_BYTE)si->rawSigned.tag.cls) << CLS_BIT_POS) & CMSCBB_TAG_CLASS_MASK) |
            ((((CVB_BYTE)si->rawSigned.tag.form) << FORM_BIT_POS) & CMSCBB_TAG_PC_MASK) |
            (CVB_BYTE)si->rawSigned.tag.code);

#if CMSCBB_SUPPORT_RSAPSS
        ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo);
#else
        ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo->e, verifyInfo->n, verifyInfo->hashAlg);
#endif
        CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Failed to init verify context .");

#if CMSCBB_SUPPORT_DIGEST_STREAM_MODE
        /* the tag type of signed attribute is implicit, so send real tag code */
        ret = CmscbbCryptoVerifyUpdate(vrfCtx, &tagCode, 1);
        CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Failed to init verify context.");

        ret = CmscbbCryptoVerifyUpdate(vrfCtx, si->rawSigned.octs + 1, si->rawSigned.len - 1);
        CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Failed to init verify context.");

        ret = CmscbbCryptoVerifyFinal(vrfCtx, si->encryptedDigest.octs, si->encryptedDigest.len, result);
        CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Failed to init verify context.");
#else
        ret = InternalCryptoVerify(vrfCtx, si, tagCode, result);
        CVB_GOTO_ERR_IF_FAIL(ret);
#endif
    }

CVB_ERR:
    CmscbbDestoryVerifyInfo(verifyInfo);
    if (vrfCtx != CVB_NULL) {
        CmscbbCryptoVerifyDestroyCtx(vrfCtx);
    }
    return ret;
}
#endif

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
CVB_STATIC CMSCBB_ERROR_CODE InternalGetVerifySignedAttrErrorCode(CmscbbP7TypeUsage p7Type)
{
    if (p7Type == CMSCBB_P7_TYPE_TS) {
        return CMSCBB_ERR_PKI_TST_CONTENT_VERIFY;
    }

    return CMSCBB_ERR_PKI_ENCAP_CONTENT_VERIFY;
}

/*
 * Prototype    : InternalVerifyAttributeSign
 * Description  : verify Signer attribute
 * Params
 *   [IN] signerInfoTs: signer info in CMS with timestamps
 *   [IN] cert: X509 certificate
 *   [IN] encodedTsInfo: encode timestamp information
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/12/26 10:17  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyAttributeSign(const CmscbbPkcs7SignedInfo *signerInfo,
    const CmscbbX509Cert *cert, const CmscbbAsnOcts *encapConInfo, CmscbbP7TypeUsage p7Type)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT vrfResult = 0;
    CmscbbVerifyDigestInfo *digestInfo = CVB_NULL;
    CVB_BYTE digest[CMSCBB_MAX_DIGEST_SIZE] = {0};
    CVB_UINT32 digestLen = 0;
    CVB_UINT32 pbDigestMaxLen = CMSCBB_MAX_DIGEST_SIZE;

    ret = CmscbbPkcs7DetachedSignatureVerify(signerInfo, cert, &vrfResult);
    if (CVB_FAILED(ret) || (CVB_BOOL)vrfResult != CVB_TRUE) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_VERIFY_FAILED, CVB_NULL);
        return CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
    }

    ret = InternalGetDigestFromSignerInfo(signerInfo, &digestInfo);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalHashContent(encapConInfo->octs, encapConInfo->len, digestInfo->digestAlgID, digest, &digestLen,
        &pbDigestMaxLen);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, CVB_NULL);

    if (digestInfo->digestSize != digestLen || CmscbbMemCmp(digestInfo->digest, digest, digestLen) != 0) {
        ret = InternalGetVerifySignedAttrErrorCode(p7Type);
        CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, CVB_NULL);
    }

CVB_ERR:
    if (digestInfo != CVB_NULL) {
        InternalFreeMdInfo(digestInfo);
    }
    return ret;
}
#endif

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckContrnType(const CmscbbPkcs7Content* p7Sign, const CmscbbPkcs7SignedInfo *si)
{
    if (CMSCBB_COMPARE_ASN_BITS(&(si->authAttrs.contentType), &(p7Sign->signedData.content.type)) != 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_VERIFY_FAILED, "eContentType is not Uniform.");
        return (CMSCBB_ERR_PKI_CMS_VERIFY_FAILED);
    }
    return CVB_SUCCESS;
}
#endif

CVB_STATIC CMSCBB_ERROR_CODE InternalX509PubKeyVerify(const CmscbbAsnOcts *tsInfo, const CmscbbPkcs7SignedInfo *siTs,
    CmscbbX509Cert *cert, CVB_BOOL *result)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT res = CVB_FALSE;
    CmscbbKeyAndAlgInfo *verifyInfo = CVB_NULL;
    ret = CmscbbCreateVerifyInfo(&verifyInfo);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    ret = CmscbbGetCmsVerifyInfo(siTs, cert, verifyInfo);
    CVB_GOTO_ERR_IF_FAIL(ret);
    ret = CmscbbCryptoVerifyQuick(tsInfo, &(siTs->encryptedDigest), verifyInfo, &res);
    CVB_GOTO_ERR_IF(CVB_FAILED(ret), ret);
    *result = (CVB_BOOL)res;
CVB_ERR:
    CmscbbDestoryVerifyInfo(verifyInfo);
    return ret;
}

CVB_STATIC CVB_UINT32 InternalGetExtendKeyUsage(CmscbbP7TypeUsage p7Type)
{
    if (p7Type == CMSCBB_P7_TYPE_TS) {
        return CMSCBB_PKI_XKU_TIMESTAMP;
    }
    if (p7Type == CMSCBB_P7_TYPE_VOUCHER) {
        return CMSCBB_PKI_XKU_VOUCHERSIGNING;
    }
    return CMSCBB_PKI_XKU_ANYEXTENDEDKEYUSAGE;
}

CVB_STATIC CVB_UINT32 InterlnalCvtUsageTypeToExtKeyUsage(CVB_INT signatureType)
{
    CVB_UINT32 map[] = { 0, CMSCBB_PKI_XKU_CODE_SIGN, CMSCBB_PKI_XKU_RELEASE_SIGN };
    if (signatureType < USAGE_BUILD_SIGNING || signatureType >= USAGE_MAX_SIGNING) {
        return 0;
    }
    return map[signatureType];
}

CVB_STATIC CVB_BOOL InternalEncapTypeIsIdData(CmscbbP7TypeUsage p7Type)
{
    if (p7Type == CMSCBB_P7_TYPE_CODE) {
        return CVB_TRUE;
    }

    return CVB_FALSE;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalEncapVerify(const CmscbbPkcs7SignedInfo *si, const CmscbbAsnOcts *encapConInfo,
    CmscbbX509Cert *cert)
{
    CVB_BOOL res = CVB_FALSE;
    CMSCBB_ERROR_CODE ret;
    ret = InternalX509PubKeyVerify(encapConInfo, si, cert, &res);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    if (res != 1) {
        return CMSCBB_ERR_PKI_TST_CONTENT_VERIFY;
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalVerifySignerInfo
 * Description  : verify signer info in timestamp
 * Params
 *   [IN] vrf:  data for verify context
 *   [IN] siTs: signer info in CMS with timestamps
 *   [IN] encodedTsInfo: encode timstamp information
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 11:43  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifySignerInfo(CmscbbVrf *vrf, const CmscbbPkcs7Content* pkcs7,
    const CmscbbPkcs7SignedInfo *si, const CmscbbAsnOcts *encapConInfo, CmscbbP7TypeUsage p7Type)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbX509Cert *cert;

    cert = CmscbbPkiFindCertByIssuerSn(vrf, &(si->issuerSn.issuer), &(si->issuerSn.sn));
    CVB_GOTO_ERR_WITH_LOG_IF((cert == CVB_NULL), CMSCBB_ERR_PKI_TST_ISSUER_NOT_FOUND)

    ret = InternalVerifyIssuerCert(vrf, cert, InternalGetExtendKeyUsage(p7Type));
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret)

    ret = CmscbbCheckAlgoPara(&(cert->toBeSigned.subjectPubKey.algorithm), &(si->signatureAlgo));
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret)

    /*
     * see RFC5652 sectoin 5.3
     * The field is optional, but it MUST be present if the content type of the EncapsulatedContentInfo
     * value being signed is not id-data.
     */
#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
    if (si->rawSigned.octs != CVB_NULL && si->authAttrs.hashValue.octs != CVB_NULL) {
        ret = InternalVerifyAttributeSign(si, cert, encapConInfo, p7Type);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
        ret = InternalCheckContrnType(pkcs7, si);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    } else {
        if (!InternalEncapTypeIsIdData(p7Type)) {
            return CMSCBB_ERR_PKI_CMS_DECODE;
        }
        ret = InternalEncapVerify(si, encapConInfo, cert);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    }
#else
    if (!InternalEncapTypeIsIdData(p7Type)) {
        return CMSCBB_ERR_PKI_NO_SIGNEDATTR;
    }
    ret = InternalEncapVerify(si, encapConInfo, cert);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

#endif

#if CMSCBB_ALLOW_NO_CHECK_TSA_CRL
    if (cert->toBeSigned.serialNumber.len > CMSCBB_MAX_SN_LEN) {
        CVB_LOG_ERROR(CMSCBB_ERR_SYS_MEM_COPY, CVB_NULL);
        return CMSCBB_ERR_SYS_MEM_COPY;
    }
    ret = (CMSCBB_ERROR_CODE)memcpy_s(vrf->tsa_cert_sn.sn, CMSCBB_MAX_SN_LEN, cert->toBeSigned.serialNumber.octs,
        cert->toBeSigned.serialNumber.len);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), CMSCBB_ERR_SYS_MEM_COPY);

    vrf->tsa_cert_sn.snLenth = cert->toBeSigned.serialNumber.len;
#endif

CVB_ERR:
    return ret;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckDigest(const CmscbbPkcs7DigestAlgoBundle* algoSet,
    const CmscbbX509AlgoIdentifier* algo)
{
    CVB_UINT32 iter = 0;
    for (; iter < algoSet->digests.num; ++iter) {
        const CmscbbX509AlgoIdentifier* digestAlgo = &(algoSet->digests.data[iter]);
        if (CMSCBB_COMPARE_ASN_OID(&(digestAlgo->algorithm), &(algo->algorithm)) == 0 &&
            CMSCBB_COMPARE_ASN_BITS(&(digestAlgo->parameters), &(algo->parameters)) == 0) {
            return CVB_SUCCESS;
        }
    }
    return CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
}

/*
 * Prototype    : InternalVerifyTimeStamp
 * Description  : verify timestamp
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] si: signer info in CMS with timestamps
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 11:47  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyTimeStamp(CmscbbVrf* vrf, const CmscbbPkcs7SignedInfo* si)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPkcs7Content* pkcs7Ts = CVB_NULL;
    CVB_UINT32 decodedLen = 0;
    SET_OF(CmscbbPkcs7SignedInfo)* signerInfos = CVB_NULL;
    CVB_INT iter = 0;
    CmscbbAsnOcts* encodedTsInfo = CVB_NULL;

    /* get timestamp from CMS */
    ret = CmscbbPkcs7DecodeSigned(si->unauthAttrs.timestamp.octs, si->unauthAttrs.timestamp.len, &pkcs7Ts, &decodedLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    /* get encoded TST info from timestamp */
    ret = CmscbbAsnDecode(pkcs7Ts->signedData.content.content.octs, pkcs7Ts->signedData.content.content.len,
                          CmscbbGetCmscbbAsnOcts(), CVB_ASN_NORMAL, (CVB_VOID**)&encodedTsInfo, &decodedLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalVerifyTstInfo(vrf, encodedTsInfo, si, pkcs7Ts);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = InternalExtractCertsAndCrlsIntoVerifyContext(vrf, pkcs7Ts);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    signerInfos = &(pkcs7Ts->signedData.signerInfos.infos);
    for (; iter < (CVB_INT)signerInfos->num; ++iter) {
        ret = InternalCheckDigest(&(pkcs7Ts->signedData.digestAlgos), &(signerInfos->data[iter].digestAlgo));
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        ret = InternalVerifySignerInfo(vrf, pkcs7Ts, &(signerInfos->data[iter]), encodedTsInfo, CMSCBB_P7_TYPE_TS);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    }

CVB_ERR:
    CmscbbFreeWithNullCheck(encodedTsInfo);
    CmscbbPkcs7FreeSigned(pkcs7Ts);
    return ret;
}

/*
 * Prototype    : InternalVerifyCms
 * Description  : verify CMS
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] p7signed: pkcs7 signed content
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 12:04  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyCms(CmscbbVrf* vrf, const CmscbbPkcs7Content* p7signed)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter = 0;
    const SET_OF(CmscbbPkcs7SignedInfo)* signerInfos = &(p7signed->signedData.signerInfos.infos);
    ret = InternalExtractCertsAndCrlsIntoVerifyContext(vrf, p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    for (; iter < (CVB_INT)(signerInfos->num); ++iter) {
        const CmscbbPkcs7SignedInfo* si = &(signerInfos->data[iter]);
        CmscbbX509Cert* cert = CVB_NULL;
        /* check DigestAlgorithm, see RFC5652 section 5.1. */
        ret = InternalCheckDigest(&(p7signed->signedData.digestAlgos), &(si->digestAlgo));
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        /* if verify succeed, the vrf will be assigned with timestamp time */
        ret = InternalVerifyTimeStamp(vrf, si);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        cert = CmscbbPkiFindCertByIssuerSn(vrf, &(si->issuerSn.issuer), &(si->issuerSn.sn));
        CVB_GOTO_ERR_WITH_LOG_IF((cert == CVB_NULL), CMSCBB_ERR_PKI_CMS_ISSUER_NOT_FOUND);

        /* author certificate must have extent usage of code sign */
        ret = InternalVerifyIssuerCert(vrf, cert, InterlnalCvtUsageTypeToExtKeyUsage(vrf->usageType));
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        ret = CmscbbCheckAlgoPara(&(cert->toBeSigned.subjectPubKey.algorithm), &(si->signatureAlgo));
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret)
        /*
         * there are two kind of signer info, one have signed attribute, the other don't;
         * they have different way to verify
         */
        if (si->authAttrs.hashValue.octs != CVB_NULL) {
#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
            CVB_INT vrfResult = 0;
            CVB_GOTO_ERR_WITH_LOG_IF((si->rawSigned.octs == CVB_NULL), CMSCBB_ERR_PKI_CMS_DECODE);

            ret = CmscbbPkcs7DetachedSignatureVerify(si, cert, &vrfResult);
            if (CVB_FAILED(ret) || (CVB_BOOL)vrfResult != CVB_TRUE) {
                CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_VERIFY_FAILED, CVB_NULL);
                return (CMSCBB_ERR_PKI_CMS_VERIFY_FAILED);
            }
            ret = InternalCheckContrnType(p7signed, si);
            CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
            ret = InternalAddSignerInfoToVerifyProcess(vrf, si);
            CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
#else
            return CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
#endif
        } else {
#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
            ret = InternalCreateVerifyHandle(vrf, si, cert);
            CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
#else
            return CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
#endif
        }
    }
    ret = CmscbbPkiVerifyCertList(vrf);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
CVB_ERR:
    return ret;
}

/*
 * Prototype    : InternalCheckParamVerifyDetachSignatureBegin
 * Description  : check verification signatures parameters
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] p7signed: Signature data in CMS format
 *   [IN] nSigLength: The length of the signature data
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 12:04  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyDetachSignatureBegin(const CmscbbVrf* vrf,
    const CVB_BYTE* signature, CVB_UINT32 sigLength)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (signature == CVB_NULL || sigLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    return InternalCheckBufLen(sigLength);
}

CMSCBB_ERROR_CODE CmscbbVerifyDetachSignatureBegin(CMSCBB_VRF_CTX ctx, const CVB_BYTE* signature, CVB_UINT32 sigLen)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CmscbbPkcs7Content* p7signed = CVB_NULL;

    ret = InternalCheckParamVerifyDetachSignatureBegin(vrf, signature, sigLen);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

#if CMSCBB_ALLOW_NO_CHECK_TSA_CRL
    ret = (CMSCBB_ERROR_CODE)memset_s(&vrf->tsa_cert_sn, sizeof(CmscbbSerialNum), 0, sizeof(CmscbbSerialNum));
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
#endif

    ret = CmscbbDecodeSignature(vrf, signature, sigLen, &p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    ret = InternalVerifyCms(vrf, p7signed);
    CmscbbPkcs7FreeSigned(p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
CVB_ERR:
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyDetachSignatureBegin);
#endif

/*
 * Prototype    : InternalCheckParmVerifyDetachSignatureUpdate
 * Description  : check parameter when update the contents of the signature
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] pbContent: The data for the signature content
 *   [IN] nContentLength: The length of the signed content fragment data
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:31  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParmVerifyDetachSignatureUpdate(const CmscbbVrf* vrf,
    const CVB_BYTE* pbContent, CVB_INT32 nContentLength)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (pbContent == CVB_NULL || nContentLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbVerifyDetachSignatureUpdate(CMSCBB_VRF_CTX ctx, const CVB_BYTE* content,
    CVB_INT32 contentLength)
{
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbVerifyDigestInfo)* digestInfoList = CVB_NULL;
    CVB_INT iter = 0;

    ret = InternalCheckParmVerifyDetachSignatureUpdate(vrf, content, contentLength);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    digestInfoList = &(vrf->vrfProc.mdInfoList);
    if (digestInfoList->num == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_UPDATE_FAILED, CVB_NULL);
        return (CMSCBB_ERR_PKI_CMS_UPDATE_FAILED);
    }

    for (; iter < (CVB_INT)digestInfoList->num; ++iter) {
        CmscbbVerifyDigestInfo* digestInfo = digestInfoList->data[iter];
        if (digestInfo->digestCtx != CVB_NULL) {
#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
            /* CMS with signed attributes */
            ret = CmscbbMdUpdate(digestInfo->digestCtx, content, (CVB_UINT32)contentLength);
            if (CVB_FAILED(ret)) {
                CVB_LOG_ERROR(ret, CVB_NULL);
                return ret;
            }
#else
            return CMSCBB_ERR_PKI_CMS_UPDATE_FAILED;
#endif
        } else {
#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
            if (digestInfo->vrfCtx == CVB_NULL) {
                return CMSCBB_ERR_PKI_CMS_UPDATE_FAILED;
            }
            /* CMS no signed attributes */
            ret = CmscbbCryptoVerifyUpdate(digestInfo->vrfCtx, content, (CVB_UINT32)contentLength);
            if (CVB_FAILED(ret)) {
                CVB_LOG_ERROR(ret, CVB_NULL);
                return ret;
            }
#else
            return CMSCBB_ERR_PKI_CMS_UPDATE_FAILED;
#endif
        }
    }

    return CVB_SUCCESS;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyDetachSignatureUpdate);
#endif

/*
 * Prototype    : InternalCheckParamVerifyDetachSignatureFinal
 * Description  : check paramters when verify detach signatures
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] res: Validation results, 1 means pass, 0 means no.
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:35  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyDetachSignatureFinal(const CmscbbVrf* vrf,
    const CVB_INT32* res)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (res == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
CVB_STATIC CMSCBB_ERROR_CODE InternalCmscbbVerifyMdFinal(const CmscbbVerifyDigestInfo* digestInfo,
    CVB_INT32* res)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BYTE digest[CMSCBB_MAX_DIGEST_SIZE] = { 0 };
    CVB_UINT32 digestLen = 0;
    CVB_UINT32 digestMaxLen = CMSCBB_MAX_DIGEST_SIZE;
    ret = CmscbbMdFinal(digestInfo->digestCtx, digest, &digestLen);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if (digestInfo->digestSize != digestLen) {
        *res = CVB_FALSE;
        return ret;
    }

    if (CmscbbMemCmp(digest, digestInfo->digest, digestInfo->digestSize) != 0) {
        *res = CVB_FALSE;
        return ret;
    }
    return ret;
}
#endif

#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
CVB_STATIC CMSCBB_ERROR_CODE InternalCmscbbVerifyCryptoFinal(const CmscbbVerifyDigestInfo* digestInfo,
    CVB_INT32* res)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT sigValid = 0;
    if (digestInfo->vrfCtx == CVB_NULL) {
        return CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
    }

    ret = CmscbbCryptoVerifyFinal(digestInfo->vrfCtx, digestInfo->signature, digestInfo->signatureLen, &sigValid);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    *res = sigValid;
    return ret;
}
#endif

CMSCBB_ERROR_CODE CmscbbVerifyDetachSignatureFinal(CMSCBB_VRF_CTX ctx, CVB_INT32* res)
{
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbVerifyDigestInfo)* digestInfoList = CVB_NULL;
    CVB_INT iter = 0;

    ret = InternalCheckParamVerifyDetachSignatureFinal(vrf, res);
    CVB_GOTO_ERR_IF(CVB_FAILED(ret), ret);

    *res = CVB_TRUE;

    digestInfoList = &(vrf->vrfProc.mdInfoList);
    for (; iter < (CVB_INT)digestInfoList->num; ++iter) {
        CmscbbVerifyDigestInfo* digestInfo = digestInfoList->data[iter];

        if (digestInfo->digestCtx != CVB_NULL) {
#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
            ret = InternalCmscbbVerifyMdFinal(digestInfo, res);
#else
            ret = CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
#endif
            if (CVB_FAILED(ret) || ((CVB_UINT32)*res == CVB_FALSE)) {
                break;
            }
        } else {
#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
            ret = InternalCmscbbVerifyCryptoFinal(digestInfo, res);
#else
            ret = CMSCBB_ERR_PKI_CMS_VERIFY_FAILED;
#endif
            if (CVB_FAILED(ret)) {
                break;
            }
        }
    }

    CMSCBB_LIST_FREE(digestInfoList, InternalFreeMdInfo);
    CVB_GOTO_ERR_WITH_LOG_IF2(CVB_FAILED(ret), ret, "Verify Signature Content failed.");

CVB_ERR:
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyDetachSignatureFinal);
#endif

#if CMSCBB_ALLOW_NO_CHECK_TSA_CRL
/*
 * Prototype    : InternalCheckParamVerifyGetTsaCertSn
 * Description  : check paramters when verify detach signatures
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] sn: serials number type
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:35  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamVerifyGetTsaCertSn(const CmscbbVrf* vrf, const CmscbbSerialNum* sn)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (sn == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbVerifyGetTsaCertSn
 * Description  : verify detach signatures with serials number type
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] sn: serials number type.
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:35  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbVerifyGetTsaCertSn(CMSCBB_VRF_CTX ctx, CmscbbSerialNum* sn)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;

    ret = InternalCheckParamVerifyGetTsaCertSn(vrf, sn);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if (vrf->tsa_cert_sn.snLenth == 0) {
        return CMSCBB_ERR_PKI_TST_ISSUER_NOT_FOUND;
    }

    ret = (CMSCBB_ERROR_CODE)memcpy_s(sn->sn, CMSCBB_MAX_SN_LEN, vrf->tsa_cert_sn.sn, vrf->tsa_cert_sn.snLenth);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    sn->snLenth = vrf->tsa_cert_sn.snLenth;
    return CVB_SUCCESS;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVerifyGetTsaCertSn);
#endif

#endif

/*
 * Prototype    : InternalFreeMdInfo
 * Description  : free CMSCBB_VERIFY_DIGEST_INFO
 * Params
 *   [IN] digestInfo: verify digest info
 * Return Value : CVB_VOID
 *   Date              Author     Modification
 *   2015/11/10 14:52  tangzonglei  Create
 */
CVB_STATIC CVB_VOID InternalFreeMdInfo(CmscbbVerifyDigestInfo* digestInfo)
{
    if (digestInfo == CVB_NULL) {
        return;
    }

#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
    if (digestInfo->digestCtx != CVB_NULL) {
        CmscbbMdDestoryCtx(digestInfo->digestCtx);
    }
#endif

#if CMSCBB_SUPPORT_NO_SIGNED_ATTR
    if (digestInfo->vrfCtx != CVB_NULL) {
        CmscbbCryptoVerifyDestroyCtx(digestInfo->vrfCtx);
    }
#endif
    CmscbbFreeWithNullCheck(digestInfo);
}

CMSCBB_ERROR_CODE CmscbbVrfCtxFree(CMSCBB_VRF_CTX ctx)
{
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;

    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    CmscbbPkiUninit(vrf);

    CMSCBB_LIST_FREE(&(vrf->vrfProc.mdInfoList), InternalFreeMdInfo);

#if (CMSCBB_SUPPORT_PEM || CMSCBB_CACHE_ASN_DATA)
    CMSCBB_LIST_FREE(&(vrf->rawSet), CmscbbFree);
#endif

    CmscbbFreeWithNullCheck(vrf);

    return CVB_SUCCESS;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbVrfCtxFree);
#endif

const CVB_CHAR* CmscbbGetVersion(CVB_VOID)
{
    return (const CVB_CHAR*)CMSCBB_VERSION;
}

#if CMSCBB_SUPPORT_CRL_COMPARE
#if CMSCBB_SUPPORT_FILE
/*
 * Prototype    : CmscbbDecodeCrlFile
 * Description  : check paramter
 * Params
 *   [IN] ctx: Validation context
 *   [IN] crlFile: CRL file path
 *   [OUT] result:  Parse the res, including all CRL information in the file
 * Return Value : return cvb_success correctly, error return error code
 * Remarks      :The CRL file may have merged a CRL published by multiple root CAs, so the return res is a list
 *               of CRL information
 *   Date              Author     Modification
 *   2015/11/09 18:42  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamDecodeCrlFile(const CmscbbVrf* vrf, const CVB_CHAR* crlFile)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (crlFile == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbDecodeCrlFile(CMSCBB_VRF_CTX ctx, const CVB_CHAR* crlFile, CmscbbCrlBundleInfo** result)
{
    CVB_BYTE* data = CVB_NULL;
    CVB_INT32 fileBytes = 0;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CMSCBB_ERROR_CODE ret;

    ret = InternalCheckParamDecodeCrlFile(vrf, crlFile);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if (result == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = InternalReadFile(crlFile, &data, &fileBytes);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbDecodeCrl(ctx, data, (CVB_UINT32)fileBytes, result);
    CmscbbFreeWithNullCheck(data);

    return ret;
}
#endif /* CMSCBB_SUPPORT_FILE */

/*
 * Prototype    : InternalCheckParamDecodeCrl
 * Description  : check paramter when decode crl
 * Params
 *   [IN] vrf: Validation context
 *   [IN] pbCrl: CRL data address
 *   [IN] nCrlLength: CRL data length
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:45  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamDecodeCrl(const CmscbbVrf* vrf, const CVB_BYTE* pbCrl,
    CVB_UINT32 nCrlLength, CmscbbCrlBundleInfo * const * pointerResult)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (pbCrl == CVB_NULL || nCrlLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    if (pointerResult == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return InternalCheckBufLen(nCrlLength);
}


CMSCBB_ERROR_CODE CmscbbDecodeCrl(CMSCBB_VRF_CTX ctx, const CVB_BYTE* crl, CVB_UINT32 crlLength,
    CmscbbCrlBundleInfo** pointerResult)
{
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbX509Cert) certList = {0};
    LIST_OF(CmscbbX509Crl) crlList = {0};
    CmscbbCrlBundleInfo* result = CVB_NULL;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CmscbbListDummy* rawSet = CVB_NULL;
#if (CMSCBB_SUPPORT_PEM || CMSCBB_CACHE_ASN_DATA)
    rawSet = &vrf->rawSet;
#endif

    ret = InternalCheckParamDecodeCrl(vrf, crl, crlLength, pointerResult);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbResetCrl(ctx);
    CVB_GOTO_ERR_IF_FAIL(ret);

    ret = CmscbbDecodeAndCheckCrl(rawSet, crl, crlLength, &certList, &crlList);
    CVB_GOTO_ERR_IF_FAIL(ret);

    ret = InternalAddCrlToStore(vrf, &crlList);
    CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);

    ret = InternalAddCertToStore(vrf, &certList, CVB_FALSE);
    CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);

    ret = CmscbbMallocWith0((CVB_VOID**)&result, sizeof(CmscbbCrlBundleInfo));
    CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);

    CmscbbUpdateCrlBundle(vrf, result, &crlList);
    *pointerResult = result;

    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return CVB_SUCCESS;
CVB_ERR:
    CmscbbFreeWithNullCheck(result);
    *pointerResult = CVB_NULL;
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbDecodeCrl);
#endif

/*
 * Prototype    : InternalCheckParamDecodeCrlToList
 * Description  : check paramter when decode crl update list;
 * Params
 *   [IN] vrf: Validation context
 *   [IN] crlParam: To decode CRL info
 *   [IN] crlUpdateInfo: CRL data length
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2021/02/27 18:45  yangdingfu  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamDecodeCrlToList(const CmscbbVrf* vrf,
    const CmscbbDecodeCrlParam* crlParam, CmscbbCrlToUpdateList * const * crlUpdateInfo)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (crlParam == CVB_NULL || crlUpdateInfo == CVB_NULL || crlParam->crl == CVB_NULL || crlParam->len == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (crlParam->needVerify == CVB_FALSE && crlParam->saveCA == CVB_TRUE) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return InternalCheckBufLen(crlParam->len);
}

/*
 * Prototype    : InternalAddCrlsToUpdateList
 * Description  : Add crls to crl update list;
 * Params
 *   [OUT] vrf: Validation context
 *   [IN] result: target crl update list to add CRL
 *   [IN] crlList: CRL list to be add.
 *   [IN] certList: Used To verify CRL if needVerifyCrl is TRUE
 *   [IN] needVerifyCrl: CRL whther need verify. if download from server,the value should be true
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2021/02/27 18:45  yangdingfu  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalAddCrlsToUpdateList(CmscbbVrf* vrf, CmscbbCrlToUpdateList* result,
    const LIST_OF(CmscbbX509Crl)* crlList, const LIST_OF(CmscbbX509Cert)* certList, CVB_BOOL needVerifyCrl)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter = 0;
    CmscbbCrlAddressInfo* addressInfo = CVB_NULL;
    CmscbbCrlInfo* crlInfo = CVB_NULL;
    CVB_INT rootCrlIter = 0;

    if (needVerifyCrl) {
        ret = InternalAddCrlToStore(vrf, crlList);
        CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);

        ret = InternalAddCertToStore(vrf, certList, CVB_FALSE);
        CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);
    }

    for (; iter < (CVB_INT)crlList->num; ++iter) {
        CmscbbX509Crl* crl = crlList->data[iter];
        addressInfo = CVB_NULL;
        if (crl == CVB_NULL) {
            continue;
        }
        if ((result->totalCount - result->certCount) >= MAX_CRL_SUPPORT) {
            CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
            return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
        }
        ret = CmscbbMallocWith0((CVB_VOID**)&addressInfo, sizeof(CmscbbCrlAddressInfo));
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);
        ret = CmscbbSetCrlAddrInfo(addressInfo, crl);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);

        if (needVerifyCrl) {
            ret = CmscbbGetCrlInfo(vrf, crl, &crlInfo);
            if (CVB_FAILED(ret)) {
                CmscbbFreeWithNullCheck(addressInfo);
                continue;
            }
            CVB_GOTO_ERR_WITH_LOG_IF(crlInfo->valid != 1, CMSCBB_ERR_PKI_CRL_INVALID);
            ret = (CMSCBB_ERROR_CODE)memcpy_s(&addressInfo->serialNum, sizeof(CmscbbSerialNum),
                &crlInfo->issuerSn, sizeof(CmscbbSerialNum));
            CVB_GOTO_ERR_IF_FAIL_LOG(ret);
            ret = CmscbbCopyRevokeList(result, &rootCrlIter, crlInfo);
            CVB_GOTO_ERR_IF_FAIL_LOG(ret);
            CmscbbCrlInfoFree(crlInfo);
            crlInfo = CVB_NULL;
        }
        result->addressInfoList[result->totalCount++] = addressInfo;
    }
    return CVB_SUCCESS;
CVB_ERR:
    CmscbbCrlInfoFree(crlInfo);
    CmscbbFreeWithNullCheck(addressInfo);
    return ret;
}

CMSCBB_ERROR_CODE CmscbbDecodeCrlToList(CMSCBB_VRF_CTX ctx, const CmscbbDecodeCrlParam* crlParam,
    CmscbbCrlToUpdateList** crlUpdateInfo)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    LIST_OF(CmscbbX509Cert) certList = {0};
    LIST_OF(CmscbbX509Crl) crlList = {0};
    CmscbbCrlToUpdateList* resultList = CVB_NULL;
    CmscbbBuf crlInfo = {0};
    CmscbbListDummy* rawSet = CVB_NULL;
    crlInfo.val = crlParam->crl;
    crlInfo.bufLen = crlParam->len;

    ret = InternalCheckParamDecodeCrlToList(vrf, crlParam, crlUpdateInfo);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbResetCrl(vrf);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

#if (CMSCBB_SUPPORT_PEM || CMSCBB_CACHE_ASN_DATA)
    rawSet = &vrf->rawSet;
#endif
    ret = CmscbbDecodeAndCheckCrl(rawSet, crlInfo.val, crlInfo.bufLen, &certList, &crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMallocWith0((CVB_VOID**)&resultList, sizeof(CmscbbCrlToUpdateList));
    CVB_GOTO_ERR_WITH_LOG_IF(ret != CVB_SUCCESS, CMSCBB_ERR_PKI_CRL_DECODE);

    ret = InternalAddCrlsToUpdateList(vrf, resultList, &crlList, &certList, crlParam->needVerify);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    if (crlParam->saveCA) {
        ret = CmscbbAddCertsToUpdateList(resultList, &certList);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    }
    *crlUpdateInfo = resultList;
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return CVB_SUCCESS;

CVB_ERR:
    (void)CmscbbCrlUpdateListFree(resultList);
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return ret;
}

/*
 * Prototype    : InternalCheckParamDecodeCrlCertInfoToList
 * Description  : Decode crl info to list
 * Params
 *   [IN] crl: CRL data address
 *   [IN] crlLength: CRL data length
 *   [IN] infoList: CRL resolution results, containing all CRL information
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author       Modification
 *   2021/10/25 11:05  wangqinyu    Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamDecodeCrlCertInfoToList(const CVB_BYTE* crl, CVB_UINT32 crlLength,
    CmscbbCrlCertInfoList * const * infoList)
{
    if (infoList == CVB_NULL || crl == CVB_NULL || crlLength == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return InternalCheckBufLen(crlLength);
}

CMSCBB_ERROR_CODE CmscbbDecodeCrlCertInfoToList(const CVB_BYTE* crl, CVB_UINT32 crlLength,
    CmscbbCrlCertInfoList** infoList)
{
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbX509Cert) certList = {0};
    LIST_OF(CmscbbX509Crl) crlList = {0};
    CmscbbListDummy rawSet = {0};
    CmscbbCrlCertInfoList* crlInfoList = CVB_NULL;

    ret = InternalCheckParamDecodeCrlCertInfoToList(crl, crlLength, infoList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMallocWith0((CVB_VOID**)&crlInfoList, sizeof(CmscbbCrlCertInfoList));
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbDecodeCertCrl(&rawSet, crl, crlLength, &certList, &crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbAddCrlsInfoToList(crlInfoList, &crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbAddCertsInfoToList(crlInfoList, &certList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    *infoList = crlInfoList;

CVB_ERR:
    if (CVB_FAILED(ret)) {
        (CVB_VOID)CmscbbCrlCertInfoListFree(crlInfoList);
        crlInfoList = CVB_NULL;
    }
    CMSCBB_LIST_FREE(&rawSet, CmscbbFree);
    CMSCBB_LIST_FREE(&certList, CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&crlList, CmscbbX509FreeCrl);
    return ret;
}

CMSCBB_ERROR_CODE CmscbbCrlCertInfoListFree(CmscbbCrlCertInfoList* infoList)
{
    CVB_INT iter = 0;
    CmscbbCrlCertInfoList* resultList = infoList;

    if (resultList == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    if (resultList->certCount > MAX_CERT_SUPPORT || resultList->crlCount > MAX_CRL_SUPPORT) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    while (iter < resultList->certCount) {
        CmscbbCertBaseInfoFree(resultList->certInfoList[iter]);
        resultList->certInfoList[iter] = CVB_NULL;
        iter++;
    }

    iter = 0;
    while (iter < resultList->crlCount) {
        CmscbbCrlBaseInfoFree(resultList->crlInfoList[iter]);
        resultList->crlInfoList[iter] = CVB_NULL;
        iter++;
    }

    CmscbbFree(resultList);
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalCheckParamCrlCompare
 * Description  : check two CRL file information which update
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] crlToUpdate: The object to compare
 *   [IN] crlOnDevice: The object to compare
 *   [IN] stat: Results of comparisons,
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2015/11/09 18:49  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamCrlCompare(const CmscbbVrf *vrf,
    const CmscbbCrlBundleInfo *crlToUpdate, const CmscbbCrlBundleInfo *crlOnDevice, const CmscbbCrlPeriodStat *stat)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if ((crlToUpdate == CVB_NULL) || (crlOnDevice == CVB_NULL) || (stat == CVB_NULL)) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (crlToUpdate->crlCount == 0 || crlOnDevice->crlCount == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CRL_EMPTY, CVB_NULL);
        return CMSCBB_ERR_PKI_CRL_EMPTY;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbCrlCompare(CMSCBB_VRF_CTX ctx, const CmscbbCrlBundleInfo *crlToUpdate,
    const CmscbbCrlBundleInfo *crlOnDevice, CmscbbCrlPeriodStat *stat)
{
    CmscbbVrf *vrf = (CmscbbVrf *)ctx;
    CMSCBB_ERROR_CODE ret;
    CmscbbCrlPeriodStat curState = SCPS_SAME;

    ret = InternalCheckParamCrlCompare(vrf, crlToUpdate, crlOnDevice, stat);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbCompareBundleIntercrossCrl(crlToUpdate, crlOnDevice, &curState);
    if (CVB_FAILED(ret)) {
        *stat = SCPS_MIX;
        return ret;
    }

    if (CmscbbFindBundleDuplicateCrlIssuer(crlToUpdate) == CVB_TRUE ||
        CmscbbFindBundleDuplicateCrlIssuer(crlOnDevice) == CVB_TRUE) {
        *stat = SCPS_MIX;
        return CMSCBB_ERR_PKI_CRL_DUPLICATE_ISSUER;
    }

    /* change state to "new" if crlToUpdate contains more item than crlOnDevice */
    if (curState == SCPS_SAME) {
        if (crlToUpdate->crlCount > crlOnDevice->crlCount) {
            curState = SCPS_NEW;
        } else if (crlToUpdate->crlCount < crlOnDevice->crlCount) {
            curState = SCPS_OLD;
        }
    }
    ret = CmscbbCheckBundleCompareResult(crlToUpdate, crlOnDevice, curState);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        *stat = SCPS_MIX;
        return ret;
    }

    *stat = curState;
    return ret;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbCrlCompare);
#endif

CMSCBB_ERROR_CODE CmscbbCrlFree(CMSCBB_VRF_CTX ctx, CmscbbCrlBundleInfo* crlBundle)
{
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CVB_INT iter = 0;

    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (crlBundle == CVB_NULL || crlBundle->crlCount > MAX_CRL_SUPPORT) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    for (; iter < crlBundle->crlCount; ++iter) {
        CmscbbCrlInfo* crlInfo = crlBundle->crlInfoList[iter];
        CmscbbCrlInfoFree(crlInfo);
    }

    CmscbbFreeWithNullCheck(crlBundle);
    return CVB_SUCCESS;
}

#if CMSCBB_IN_KERNEL
EXPORT_SYMBOL(CmscbbCrlFree);
#endif

/*
 * Prototype    : InternalCheckCrlUpdateCompare
 * Description  : check CRL list information which will be used compare
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] crlToUpdate: The object to compare
 *   [IN] crlOnDevice: The object to compare
 *   [OUT] crlResult: Results of comparisons,
 * Return Value : return cvb_success correctly, error return error code
 *   Date              Author     Modification
 *   2021/02/09 18:49  yangdingfu  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCrlUpdateCompare(const CmscbbVrf *vrf,
    const CmscbbCrlToUpdateList* crlToUpdate, const CmscbbCrlToUpdateList* crlOnDevice,
    CmscbbCrlToUpdateList * const * crlResult)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if ((crlToUpdate == CVB_NULL) || (crlOnDevice == CVB_NULL) || (crlResult == CVB_NULL)) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbCheckCrlUpdateList(crlToUpdate);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    return CmscbbCheckCrlUpdateList(crlOnDevice);
}

CMSCBB_ERROR_CODE CmscbbCrlUpdateListCompare(CMSCBB_VRF_CTX ctx, const CmscbbCrlToUpdateList* crlToUpdate,
    const CmscbbCrlToUpdateList* crlOnDevice, CmscbbCrlToUpdateList** crlResult)
{
    CVB_INT i;
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CmscbbCrlToUpdateList* tmpCrlResult = CVB_NULL;
    ret = InternalCheckCrlUpdateCompare(vrf, crlToUpdate, crlOnDevice, crlResult);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    ret = CmscbbMallocWith0((CVB_VOID**)&tmpCrlResult, sizeof(CmscbbCrlToUpdateList));
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    /* If crl exist in onDevice list but not exist in toUpdateList, copy all onDevice crl first.
    */
    for (i = 0; i < crlOnDevice->totalCount; i++) {
        ret = CmscbbAddCertCrlToUpdateList(tmpCrlResult, crlOnDevice->addressInfoList[i], crlToUpdate);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    }

    ret = CmscbbCrlListCompare(crlToUpdate, crlOnDevice, tmpCrlResult);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    *crlResult = tmpCrlResult;
    return CVB_SUCCESS;
CVB_ERR:
    (void)CmscbbCrlUpdateListFree(tmpCrlResult);
    return ret;
}

CMSCBB_ERROR_CODE CmscbbCrlUpdateListFree(CmscbbCrlToUpdateList* crlList)
{
    CVB_INT iter;

    if (crlList == CVB_NULL || crlList->certCount > MAX_CERT_SUPPORT ||
        crlList->totalCount - crlList->certCount > MAX_CRL_SUPPORT) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    for (iter = 0; iter < crlList->totalCount; iter++) {
        CmscbbFreeWithNullCheck(crlList->addressInfoList[iter]);
        crlList->addressInfoList[iter] = CVB_NULL;
    }

    for (iter = 0; iter < MAX_REVOKE_LIST_NUM; iter++) {
        if (crlList->revokeList[iter].revokeCount != 0) {
            CmscbbFreeWithNullCheck(crlList->revokeList[iter].snList);
        }
    }

    CmscbbFreeWithNullCheck(crlList);
    return CVB_SUCCESS;
}

#if CMSCBB_FOR_WIRELESS
static CMSCBB_ERROR_CODE InternalGetCertInfoFromUnauthAttr(const CmscbbPkcs7SignedInfo *si,
    LIST_OF(CmscbbCertNameInfo) *certList)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPkcs7Content* pkcs7Ts = CVB_NULL;
    const LIST_OF(CmscbbX509Cert) *timeCerts = CVB_NULL;
    CVB_UINT32 decodedLen;

    ret = CmscbbPkcs7DecodeSigned(si->unauthAttrs.timestamp.octs, si->unauthAttrs.timestamp.len, &pkcs7Ts,
        &decodedLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    timeCerts = &pkcs7Ts->signedData.certificates.certs;
    ret = CmscbbGetCertNameInfo(timeCerts, certList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
CVB_ERR:
    CmscbbPkcs7FreeSigned(pkcs7Ts);
    return ret;
}

static CMSCBB_ERROR_CODE InternalGetCertInfoFromSig(CMSCBB_VRF_CTX ctx, const CVB_BYTE* sig, const CVB_UINT32 siglen,
    LIST_OF(CmscbbCertNameInfo)* certList)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPkcs7Content* p7signed = NULL;
    CVB_UINT32 iter = 0;
    SET_OF(CmscbbPkcs7SignedInfo)* signerInfos = NULL;
    LIST_OF(CmscbbX509Cert)* certs = NULL;
    ret = CmscbbDecodeSignature(ctx, sig, siglen, &p7signed);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    certs = &p7signed->signedData.certificates.certs;
    ret = CmscbbGetCertNameInfo(certs, certList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    signerInfos = &(p7signed->signedData.signerInfos.infos);
    for (; iter < signerInfos->num; iter++) {
        ret = InternalGetCertInfoFromUnauthAttr(&p7signed->signedData.signerInfos.infos.data[iter], certList);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    }

    CVB_GOTO_ERR_WITH_LOG_IF(certList->num == 0, CMSCBB_ERR_PKI_CERT_NOT_FOUND);
CVB_ERR:
    CmscbbPkcs7FreeSigned(p7signed);
    return ret;
}

static CMSCBB_ERROR_CODE InternalCheckObtainMinimumCrlSet(CMSCBB_VRF_CTX ctx, const CmscbbBufferParam *sig,
    const CmscbbBufferParam *crl, CmscbbCrlToUpdateList** crlResult)
{
    CVB_BOOL legalFlag = CVB_FALSE;
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(ctx);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    legalFlag = (sig == CVB_NULL) || (crl == CVB_NULL) || (sig->buf == CVB_NULL) || (crl->buf == CVB_NULL) ||
        (crlResult == CVB_NULL);
    if (legalFlag == CVB_TRUE) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    ret = InternalCheckBufLen(sig->len);
    if (ret != CVB_SUCCESS) {
        return ret;
    }
    return InternalCheckBufLen(crl->len);
}

CMSCBB_ERROR_CODE CmscbbObtainMinimumCrlSet(CMSCBB_VRF_CTX ctx, const CmscbbBufferParam *sig,
    const CmscbbBufferParam *crl, CmscbbCrlToUpdateList** crlResult)
{
    CmscbbCrlToUpdateList* crlRes = CVB_NULL;
    CmscbbCrlToUpdateList* crlList = CVB_NULL;
    CmscbbDecodeCrlParam param = {0, 0, 0, 0};
    LIST_OF(CmscbbCertNameInfo) certList = {0};
    CMSCBB_ERROR_CODE ret = InternalCheckObtainMinimumCrlSet(ctx, sig, crl, crlResult);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    param.crl = crl->buf;
    param.len = crl->len;
    param.needVerify = CVB_TRUE;
    param.saveCA = CVB_TRUE;
    ret = InternalGetCertInfoFromSig(ctx, sig->buf, sig->len, &certList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    ret = CmscbbDecodeCrlToList(ctx, &param, &crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    ret = CmscbbCheckCrlUpdateList(crlList);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbObtainRelatedCrl(ctx, crlList, &certList, &crlRes);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);
    CmscbbCrlUpdateListFree(crlList);
    CMSCBB_LIST_FREE(&certList, CmscbbFree);
    *crlResult = crlRes;
    return ret;
CVB_ERR:
    CmscbbCrlUpdateListFree(crlList);
    CmscbbCrlUpdateListFree(crlRes);
    CMSCBB_LIST_FREE(&certList, CmscbbFree);
    return ret;
}
#endif
#endif /* CMSCBB_SUPPORT_CRL_COMPARE */

#if CMSCBB_SUPPORT_FILE
#define MAX_PKI_FILE_LEN (2 * 1000000)

/*
 * Prototype    : InternalReadFile
 * Description  : read file's content into memory
 * Params
 *   [IN] filePath: file path
 *   [OUT] data: file data object
 *   [OUT] size: the size of file
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 14:59  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalReadFile(const CVB_CHAR* filePath, CVB_BYTE** data, CVB_INT32* size)
{
    CVB_BYTE* digital = CVB_NULL;
    CVB_UINT32 fileSizeLen;
    CMSCBB_ERROR_CODE ret;
    CVB_FILE_HANDLE file;

    if (*filePath == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_SYS_FILE_OPEN, "Empty file path.");
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    file = CmscbbFileOpen(filePath, (const CVB_CHAR*)"rb");
    if (file == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_SYS_FILE_OPEN, "Can not open file.");
        return CMSCBB_ERR_SYS_FILE_OPEN;
    }

    fileSizeLen = (CVB_UINT32)CmscbbFileGetSize(file);
    CVB_GOTO_ERR_WITH_LOG_IF(fileSizeLen == 0, CMSCBB_ERR_SYS_FILE_GET_SIZE);
    CVB_GOTO_ERR_WITH_LOG_IF(MAX_PKI_FILE_LEN < fileSizeLen, CMSCBB_ERR_SYS_FILE_TOO_LARGE);

    ret = CmscbbMalloc((CVB_VOID**)&digital, fileSizeLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    CVB_GOTO_ERR_WITH_LOG_IF(fileSizeLen != CmscbbFileRead(digital, fileSizeLen, file), CMSCBB_ERR_SYS_FILE_READ);

    *size = (CVB_INT32)fileSizeLen;
    *data = digital;
    InternalCloseFile(file);
    return ret;
CVB_ERR:
    CmscbbFreeWithNullCheck(digital);
    InternalCloseFile(file);
    return ret;
}
#endif

CVB_STATIC void InteralArrayCopy(LIST_OF(CmscbbX509Cert)* destList, LIST_OF(CmscbbX509Cert)* srcList)
{
    CVB_UINT32 iter = 0;

    for (; iter < srcList->num; iter++) {
        destList->data[iter] = srcList->data[iter];
    }
    destList->num = srcList->num;
}

CMSCBB_ERROR_CODE CmscbbResetCert(CMSCBB_VRF_CTX ctx, CVB_BOOL isResetRoot)
{
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbX509Cert)* certList = NULL;
    LIST_OF(CmscbbX509Cert) certBachup = {0, {0}};
    CVB_UINT32 iter;

    if (ctx == NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    ret = CmscbbGetCertAndCrlList(ctx, &certList, NULL);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    for (iter = 0; iter < certList->num; ++iter) {
        CmscbbX509Cert* cert = certList->data[iter];
        CVB_BOOL isSelfSigned = CVB_FALSE;

        if (cert == CVB_NULL) {
            continue;
        }

        if (!isResetRoot) {
            ret = CmscbbX509IsSelfSigned(cert, &isSelfSigned);
            if (CVB_FAILED(ret)) {
                CVB_LOG_ERROR(ret, "Failed to check self sign for certificate.");
                return ret;
            }
            if (isSelfSigned == CVB_TRUE) {
                certBachup.data[certBachup.num++] = cert;
                certList->data[iter] = NULL;
                continue;
            }
        }

        CmscbbX509FreeCert(cert);
        certList->data[iter] = NULL;
    }
    InteralArrayCopy(certList, &certBachup);
    return ret;
}

CMSCBB_ERROR_CODE CmscbbResetCrl(CMSCBB_VRF_CTX ctx)
{
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbX509Crl)* crlList = NULL;
    if (ctx == NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbGetCertAndCrlList(ctx, NULL, &crlList);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }

    CMSCBB_LIST_FREE(crlList, CmscbbX509FreeCrl);
    return ret;
}
static CMSCBB_ERROR_CODE InternalObtainVoucherContent(const CmscbbAsnOid* type, CmscbbAsnOcts* content,
    CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVoucherContent* vouCon = NULL;
    CVB_BYTE* oidBuf = NULL;
    CVB_BYTE* contentBuff = NULL;

    ret = CmscbbMallocWith0((void **)&(oidBuf), type->len);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    ret = (CMSCBB_ERROR_CODE)memcpy_s(oidBuf, type->len, type->octs, type->len);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMallocWith0((void **)&contentBuff, content->len);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    ret = (CMSCBB_ERROR_CODE)memcpy_s(contentBuff, content->len, content->octs, content->len);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbMallocWith0((void **)&vouCon, sizeof(CmscbbVoucherContent));
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    vouCon->contentType.len = type->len;
    vouCon->contentType.buf = oidBuf;
    vouCon->content.len = content->len;
    vouCon->content.buf = contentBuff;
    *voucherContent = vouCon;
    return CVB_SUCCESS;
CVB_ERR:
    CmscbbFreeWithNullCheck(oidBuf);
    CmscbbFreeWithNullCheck(contentBuff);
    CmscbbFreeWithNullCheck(vouCon);
    return ret;
}

static CMSCBB_ERROR_CODE InternalVerifyVoucherSignature(CmscbbVrf* vrf, const CmscbbPkcs7Content* p7signed,
    CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter = 0;
    CmscbbAsnOcts* content = NULL;
    CVB_UINT32 contentLen;
    CmscbbAsn1Item* cmscbbAsnOcts = CmscbbGetCmscbbAsnOcts();
    const SET_OF(CmscbbPkcs7SignedInfo)* signerInfos = &(p7signed->signedData.signerInfos.infos);

    if (AOID_CT_ANIMAJSONVOUCHER != CmscbbFindAoid(&p7signed->signedData.content.type)) {
        ret = CMSCBB_ERR_PKI_WRONG_ECONTYPE;
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_WRONG_ECONTYPE, "the voucher eContentType is not JSON.");
        goto CVB_ERR;
    }
    ret = InternalExtractCertsAndCrlsIntoVerifyContext(vrf, p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    for (; iter < (CVB_INT)(signerInfos->num); ++iter) {
        const CmscbbPkcs7SignedInfo* si = &(signerInfos->data[iter]);
        /* check DigestAlgorithm, see RFC5652 section 5.1. */
        ret = InternalCheckDigest(&(p7signed->signedData.digestAlgos), &(si->digestAlgo));
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        /* if verify succeed, the vrf will be assigned with timestamp time */
        ret = InternalVerifyTimeStamp(vrf, si);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

        if (p7signed->signedData.content.content.len == 0 || p7signed->signedData.content.content.octs == NULL) {
            ret = CMSCBB_ERR_PKI_NO_ECONTENT;
            CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
        }

        ret = CmscbbAsnDecode(p7signed->signedData.content.content.octs, p7signed->signedData.content.content.len,
            cmscbbAsnOcts, CVB_ASN_NORMAL, (CVB_VOID**) &content, &contentLen);
        CVB_GOTO_ERR_IF_FAIL_LOG(ret);

        ret = InternalVerifySignerInfo(vrf, p7signed, si, content, CMSCBB_P7_TYPE_VOUCHER);
        CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
    }
    ret = InternalObtainVoucherContent(&p7signed->signedData.content.type, content, voucherContent);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
CVB_ERR:
    CmscbbFreeWithNullCheck(content);
    return ret;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckVoucherSignatureQuick(CMSCBB_VRF_CTX ctx, const CVB_BYTE* sig,
    CVB_UINT32 sigLen, CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(ctx);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (sig == NULL || voucherContent == NULL || sigLen == 0) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return InternalCheckBufLen(sigLen);
}

CMSCBB_ERROR_CODE CmscbbVerifyVoucherSignatureQuick(CMSCBB_VRF_CTX ctx, const CVB_BYTE* sig, CVB_UINT32 sigLen,
    CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CmscbbPkcs7Content* p7signed = NULL;

    ret = InternalCheckVoucherSignatureQuick(ctx, sig, sigLen, voucherContent);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbDecodeSignature(vrf, sig, sigLen, &p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);

    ret = InternalVerifyVoucherSignature(vrf, p7signed, voucherContent);
    CmscbbPkcs7FreeSigned(p7signed);
    CVB_GOTO_ERR_WITH_LOG_IF(CVB_FAILED(ret), ret);
CVB_ERR:
    return ret;
}

#if CMSCBB_SUPPORT_FILE
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckVoucherSignatureQuickFile(CMSCBB_VRF_CTX ctx, const CVB_CHAR* sigFile,
    CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(ctx);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (sigFile == NULL || voucherContent == NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbVerifyVoucherSignatureQuickFile(CMSCBB_VRF_CTX ctx, const CVB_CHAR* sigfile,
    CmscbbVoucherContent** voucherContent)
{
    CMSCBB_ERROR_CODE ret;
    CMSCBB_ERROR_CODE returnValue;
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CVB_BYTE* sig = CVB_NULL;
    CVB_INT32 sigLen = 0;
    ret = InternalCheckVoucherSignatureQuickFile(ctx, sigfile, voucherContent);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = InternalReadFile(sigfile, &sig, &sigLen);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

    ret = CmscbbVerifyVoucherSignatureQuick(vrf, sig, (CVB_UINT32)sigLen, voucherContent);
    CVB_GOTO_ERR_IF_FAIL_LOG(ret);

CVB_ERR:
    returnValue = CmscbbResetCert(ctx, CVB_FALSE);
    if (CVB_FAILED(returnValue)) {
        CVB_LOG_ERROR(returnValue, "reset failed!");
        ret = CMSCBB_ERR_CONTEXT_RESET_FAILED;
    }
    returnValue = CmscbbResetCrl(ctx);
    if (CVB_FAILED(returnValue)) {
        CVB_LOG_ERROR(returnValue, "reset failed!");
        ret = CMSCBB_ERR_CONTEXT_RESET_FAILED;
    }
    CmscbbFreeWithNullCheck(sig);
    return ret;
}
#endif

void CmscbbDestoryVoucherContent(CmscbbVoucherContent* voucherContent)
{
    if (voucherContent == NULL) {
        return;
    }
    CmscbbFreeWithNullCheck(voucherContent->content.buf);
    voucherContent->content.buf = NULL;
    CmscbbFreeWithNullCheck(voucherContent->contentType.buf);
    voucherContent->contentType.buf = NULL;
    CmscbbFreeWithNullCheck(voucherContent);
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamSetSignatureType(const CmscbbVrf* vrf,
    CmscbbSignatureUsageType usageType)
{
    CMSCBB_ERROR_CODE ret = InternalCheckVerifyOjbect(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (usageType < USAGE_BUILD_SIGNING || usageType >= USAGE_MAX_SIGNING) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbSetSignatureUsageType(CMSCBB_VRF_CTX ctx, CmscbbSignatureUsageType usageType)
{
    CmscbbVrf* vrf = (CmscbbVrf*)ctx;
    CMSCBB_ERROR_CODE ret = InternalCheckParamSetSignatureType(ctx, usageType);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    vrf->usageType = usageType;
    return CVB_SUCCESS;
}
